﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CPPEI.Update.WCF
{
    /// <summary>
    /// 下载进行中的回调
    /// </summary>
    /// <param name="fileName"></param>
    /// <param name="start"></param>
    /// <param name="length"></param>
    /// <param name="totalSize"></param>
    /// <param name="netSpeed">kb/s</param>
    public delegate void OnDownloadingCallback(string fileName, long start, int length, long totalSize, double netSpeed);

    /// <summary>
    /// 下载出错的回调
    /// </summary>
    /// <param name="errorMsg"></param>
    /// <param name="start"></param>
    /// <param name="length"></param>
    /// <param name="totalSize"></param>
    public delegate void OnDownloadingFailureCallback(string errorMsg, long start, int length, long totalSize);

    /// <summary>
    /// 下载完毕的回调
    /// </summary>
    /// <param name="fileName"></param>
    /// <param name="totalSize"></param>
    public delegate void OnDownloadingFinishedCallback(string fileName, long totalSize);

    /// <summary>
    /// 下载线程
    /// </summary>
    public class DownloadThread
    {
        private WCFServiceRef.UpdateServiceClient _updateServiceClient;

        /// <summary>
        /// 下载进度事件
        /// </summary>
        public event OnDownloadingCallback Progress;

        /// <summary>
        /// 下载失败事件
        /// </summary>
        public event OnDownloadingFailureCallback Failure;

        public event OnDownloadingFinishedCallback Finished;

        Thread _downloadThread;

        private bool _isRunning = false;

        private string _product;
        private string _appType;
        private string _fromVersion;
        private string _toVersion;
        private string _pathFileSavePath;

        /// <summary>
        /// 一个包的数据长度
        /// </summary>
        private const int SegmentLength = 4096;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        public DownloadThread(string ip, int port)
        {
            _updateServiceClient = CreateUpdateServiceClient(ip, port);
        }

        /// <summary>
        /// 终止下载线程
        /// </summary>
        public void AbortThread( )
        {
            if (_downloadThread != null)
            {
                _downloadThread.Abort();
               
            }
        }

        public void Sleep(int sleepValue)
        {
            Thread.Sleep(sleepValue);
        }

        /// <summary>
        /// 创建访问更新服务器的客户端对象
        /// </summary>
        /// <param name="hostConfig"></param>
        /// <returns></returns>
        private static WCFServiceRef.UpdateServiceClient CreateUpdateServiceClient(string ip, int port)
        {
            System.ServiceModel.Channels.Binding tcpBinding = new NetTcpBinding();
            (tcpBinding as NetTcpBinding).Security.Mode = SecurityMode.None;
            (tcpBinding as NetTcpBinding).MaxBufferSize = 1024 * 1024 * 1024;
            (tcpBinding as NetTcpBinding).MaxReceivedMessageSize = 1024 * 1024 * 1024;
            EndpointAddress address = new EndpointAddress(string.Format("net.tcp://{0}:{1}/{2}/", ip, port, "UpdateService"));

            WCFServiceRef.UpdateServiceClient updateServiceClient = new WCFServiceRef.UpdateServiceClient(tcpBinding, address);
            return updateServiceClient;
        }

        /// <summary>
        /// 下载增量式更新包文件
        /// </summary>
        /// <param name="product">产品名</param>
        /// <param name="appType">程序名</param>
        /// <param name="fromVersion">起始版本</param>
        /// <param name="toVersion">目标版本</param>
        /// <param name="patchFileSavePath">补丁文件本地的保存地址</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public void DownloadIncrementalPatch(string product, 
            string appType, 
            string fromVersion, 
            string toVersion, 
            string patchFileSavePath)
        {
            if (_isRunning)
            {
                throw new Exception(string.Format("正在下载文件中,file:{0}", _pathFileSavePath));
            }

            if (File.Exists(patchFileSavePath))
            {
                try
                {
                    File.Delete(patchFileSavePath);
                }
                catch(Exception ex)
                {
                    throw new Exception(string.Format("文件:{0}已存在，删除失败:{1}", _pathFileSavePath, ex));
                }
            }

            _downloadThread = new Thread(RunDownloadIncrementalPatch);

            _product = product;
            _appType = appType;
            _fromVersion = fromVersion;
            _toVersion = toVersion;
            _pathFileSavePath = patchFileSavePath;

            _isRunning = true;
            _downloadThread.Start();
        }

        private void RunDownloadIncrementalPatch()
        {
            byte[] data = new byte[SegmentLength];
            long startIndex = 0;
            long totalSize = 0;
            string fileName = string.Empty;

            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();

            using (FileStream fileStream = new FileStream(_pathFileSavePath, FileMode.CreateNew))
            {
                WCFServiceRef.DownloadResult result = null;
                try
                {
                    watch.Restart();
                    result = _updateServiceClient.DownloadIncrementalPatchFileSegment(_product,
                        _appType,
                        _fromVersion,
                        _toVersion,
                        startIndex);
                    watch.Stop();
                    totalSize = result.TotalSize;
                    fileStream.Write(result.Data, 0, result.Length);
                    double speed = result.Length / watch.ElapsedMilliseconds / 1000 / 1000;
                    if (Progress != null)
                    {
                        Progress(result.FileName, startIndex, result.Length, totalSize, speed);
                    }
                }
                catch (Exception ex)
                {
                    if (Failure != null)
                    {
                        Failure(ex.Message, startIndex, 0, totalSize);
                    }
                }

                if (result != null)
                {
                    fileName = result.FileName;
                    totalSize = result.TotalSize;
                    startIndex += result.Length;
                }
                while (startIndex < totalSize)
                {
                    try
                    {
                        watch.Restart();
                        result = _updateServiceClient.DownloadIncrementalPatchFileSegment(_product,
                            _appType,
                            _fromVersion,
                            _toVersion,
                            startIndex);
                        watch.Stop();
                        fileStream.Write(result.Data, 0, result.Length);
                        double speed = 0;
                        if (watch.ElapsedMilliseconds != 0)
                            speed = result.Length / watch.ElapsedMilliseconds / 1000 / 1000;
                        if (Progress != null)
                        {
                            Progress(result.FileName, startIndex, result.Length, totalSize, speed);
                        }
                    }
                    catch (Exception ex)
                    {
                        if (Failure != null)
                        {
                            Failure(ex.Message, startIndex, 0, totalSize);
                        }
                    }

                    if (result != null)
                    {
                        totalSize = result.TotalSize;
                        startIndex += result.Length;
                    }
                }
            }

            if (Finished != null)
            {
                Finished(fileName, totalSize);
            }
            _isRunning = false;
        }

        /// <summary>
        /// 下载完整更新包文件
        /// </summary>
        /// <param name="product">产品名</param>
        /// <param name="appType">程序名</param>
        /// <param name="toVersion">目标版本</param>
        /// <param name="patchFileSavePath">补丁文件本地的保存地址</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public void DownloadFullPatch(string product,
            string appType,
            string toVersion,
            string patchFileSavePath)
        {
            if (_isRunning)
            {
                throw new Exception(string.Format("正在下载文件中,file:{0}", _pathFileSavePath));
            }

            if (File.Exists(patchFileSavePath))
            {
                try
                {
                    File.Delete(patchFileSavePath);
                }
                catch (Exception ex)
                {
                    throw new Exception(string.Format("文件:{0}已存在，删除失败:{1}", _pathFileSavePath, ex));
                }
            }

            _downloadThread = new Thread(RunDownloadFullPatch);

            _product = product;
            _appType = appType;
            _toVersion = toVersion;
            _pathFileSavePath = patchFileSavePath;

            _isRunning = true;
            _downloadThread.Start();
        }

        private void RunDownloadFullPatch()
        {
            byte[] data = new byte[SegmentLength];
            long startIndex = 0;
            long totalSize = 0;
            string fileName = string.Empty;
            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();

            using (FileStream fileStream = new FileStream(_pathFileSavePath, FileMode.CreateNew))
            {
                WCFServiceRef.DownloadResult result = null;
                try
                {
                    watch.Restart();
                    result = _updateServiceClient.DownloadFullPatchFileSegment(_product,
                        _appType,
                        _toVersion,
                        startIndex);
                    watch.Stop();
                    totalSize = result.TotalSize;
                    fileStream.Write(result.Data, 0, result.Length);
                    double speed = result.Length / watch.ElapsedMilliseconds / 1000 / 1000;
                    if (Progress != null)
                    {
                        Progress(result.FileName, startIndex, result.Length, totalSize, speed);
                    }
                }
                catch (Exception ex)
                {
                    if (Failure != null)
                    {
                        Failure(ex.Message, startIndex, 0, totalSize);
                    }
                }

                if (result != null)
                {
                    fileName = result.FileName;
                    totalSize = result.TotalSize;
                    startIndex += result.Length;
                }
                while (startIndex < totalSize)
                {
                    try
                    {
                        watch.Restart();
                        result = _updateServiceClient.DownloadFullPatchFileSegment(_product,
                            _appType,
                            _toVersion,
                            startIndex);
                        watch.Stop();
                        fileStream.Write(result.Data, 0, result.Length);
                        double speed = 0;
                        if(watch.ElapsedMilliseconds != 0)
                          speed = result.Length / watch.ElapsedMilliseconds / 1000 / 1000;
                        if (Progress != null)
                        {
                            Progress(result.FileName, startIndex, result.Length, totalSize, speed);
                        }
                    }
                    catch (Exception ex)
                    {
                        if (Failure != null)
                        {
                            Failure(ex.Message, startIndex, 0, totalSize);
                        }
                    }

                    if (result != null)
                    {
                        totalSize = result.TotalSize;
                        startIndex += result.Length;
                    }
                }
            }

            if (Finished != null)
            {
                Finished(fileName, totalSize);
            }
            _isRunning = false;
        }
    }
}
